import { BundleAsset } from "./BundleAsset";
import { NormalAsset } from "./NormalAsset";

import * as cc from "cc";
declare let CC_EDITOR;
type Bundle = cc.AssetManager.Bundle;

declare global {

    type CCAssetType<T = cc.Asset> = cc.__private._cocos_core_asset_manager_shared__AssetType<T>;
    type CCIAssetOptions = cc.__private._cocos_core_asset_manager_shared__IAssetOptions;

    type CCProgressCallback = cc.__private._cocos_core_asset_manager_shared__ProgressCallback;
    type CCCompleteCallbackWithData<T = any> = cc.__private._cocos_core_asset_manager_shared__CompleteCallbackWithData;
    type RequestItem = cc.AssetManager.RequestItem;

    type LoadBundleAssetCompleteFunc = (err: Error | null, bundle: BundleAsset | null) => void;
    type LoadBundleCompleteFunc = (err: Error | null, bundle: Bundle | null) => void;

    type CCCompleteCallbackNoData = cc.__private._cocos_core_asset_manager_shared__CompleteCallbackNoData;


    interface IParameters<T> {
        options: CCIAssetOptions;
        onProgress: CCProgressCallback | null;
        onComplete: T | null;
    }

    interface LoaderPlugin {
        name: string;
        onLoadComplete(loader: AssetLoader, path: string, asset: cc.Asset, bundle: Bundle): void;
        onLoadDirComplete(loader: AssetLoader, path: string, assets: cc.Asset[], bundle: Bundle): void;
        onLoadArrayComplete(loader: AssetLoader, path: string[], assets: cc.Asset[], bundle: Bundle): void;
        onLoadSceneComplete(loader: AssetLoader, sceneName: string, scene: cc.SceneAsset, bundle: Bundle): void;

        onRelease(loader: AssetLoader, path: string, asset: cc.Asset): void;
    }
}


class Cache {
    // 考虑到 相同路径不同类型的资源，所以这里使用数组形式
    map: Map<string, Array<NormalAsset>>;
    constructor() {
        this.map = new Map();
    }
    set(path, asset: NormalAsset) {
        let has = this.map.has(path);
        let assets: NormalAsset[] = null;
        if (has) {
            assets = this.map.get(path);
        } else {
            assets = [];
            this.map.set(path, assets);
        }
        assets.push(asset);
    }
    get(path, type: CCAssetType): NormalAsset {
        let has = this.map.has(path);
        if (has) {
            let assets = this.map.get(path);
            let typeName = cc.js.getClassName(type)
            for (let i = 0; i < assets.length; i++) {
                const asset = assets[i];
                let assetClassName = cc.js.getClassName(asset.getAsset());
                if (assetClassName == typeName) {
                    return asset;
                }
            }
        }
        return null;
    }

    delete(path: string, type: CCAssetType) {
        let has = this.map.has(path);
        if (has) {
            let assets = this.map.get(path);
            let typeName = cc.js.getClassName(type)
            for (let i = 0; i < assets.length; i++) {
                const asset = assets[i];
                let assetClassName = cc.js.getClassName(asset.getAsset());
                if (assetClassName == typeName) {
                    assets.splice(i, 1);
                    if (!assets.length) {
                        this.map.delete(path);
                    }
                    return asset;
                }
            }
        }
        return null;
    }

    forEach(callbackfn: (value: NormalAsset, key: string, map: Cache) => void, thisArg?: any) {
        this.map.forEach((arr, key) => {
            for (let i = 0; i < arr.length; i++) {
                const asset = arr[i];
                callbackfn(asset, key, this);
            }
        }, thisArg);
    }

    clear() {
        this.map.clear();
    }

}


export class AssetLoader {
    protected static loadedBundles: Map<string, BundleAsset> = new Map();
    protected static bundleVersions: Map<string, string> = null!;


    public static getBundleVersions(bundleName: string): string | undefined {
        if (this.bundleVersions == null) return null;
        return this.bundleVersions.get(bundleName);
    }

    //删除bundle
    public static removeBundle(nameOrUrl: string) {
        let asset = this.loadedBundles.get(nameOrUrl);
        if (asset) {
            this.loadedBundles.delete(nameOrUrl);
            if (nameOrUrl != cc.resources.name)
                cc.assetManager.removeBundle(asset.getBundle());
        }
    }

    /**
    * 加载 bundle
    * @param bundleName 
    */
    public static loadBundle(bundleName: string | Bundle, onComplete: LoadBundleCompleteFunc) {
        this.loadBundleAsset(bundleName, (err: Error | null, bundleAsset: BundleAsset | null) => {
            if (err == null) {
                let bundle = bundleAsset.getBundle();
                onComplete?.(null, bundle);
            } else {
                onComplete?.(err, null);
            }
        });
    }
    /**
    * 加载 包装 Bundle
    * @param bundleName 
    */
    protected static loadBundleAsset(bundleName: string | Bundle, onComplete: LoadBundleAssetCompleteFunc) {
      
        if (!bundleName) {
            bundleName = cc.resources.name;
        }

        if (bundleName instanceof cc.AssetManager.Bundle) {
            bundleName = bundleName.name;
        }
        let bundle = this.loadedBundles.get(bundleName);
        if (bundle) {
            onComplete?.(null, bundle);
        } else {
            if (bundleName == cc.resources.name) {
                bundle = new BundleAsset(bundleName, cc.resources);
                this.loadedBundles.set(bundleName, bundle);
                onComplete?.(null, bundle);
            } else {
                let options: any = {}
                // if(onprogress){
                //     options.onFileProgress = (loaded: number, total: number)=>{
                //         onprogress(loaded/total);
                //     }
                // }
                let version = this.getBundleVersions(bundleName)
                if (version) {
                    options.version = version;
                }

                cc.assetManager.loadBundle(bundleName, options, (err: Error | null, data: Bundle) => {
                    if (err == null) {
                        bundle = new BundleAsset(bundleName as string, data);
                        this.loadedBundles.set(bundleName as string, bundle);
                        onComplete?.(null, bundle);
                    } else {
                        onComplete?.(err, null);
                    }
                });
            }
        }
    }

    public removeBundle(nameOrUrl: string) {
        return AssetLoader.removeBundle(nameOrUrl);
    }

    public loadBundle(bundleName: string | Bundle, onComplete: LoadBundleCompleteFunc) {
        return AssetLoader.loadBundle(bundleName, onComplete);
    }

    public loadBundleAssets(bundleName: string | Bundle, onComplete: LoadBundleAssetCompleteFunc) {
        return AssetLoader.loadBundleAsset(bundleName, onComplete);
    }


    protected _plugins: LoaderPlugin[] = [];

    protected _cache: Cache = new Cache();

    protected _loadingSet = new Set();
    protected _lastLoadingSet = new Set();
    protected _loadCount = 0;

    public registerPlugin(plugins: LoaderPlugin | LoaderPlugin[]) {
        if (CC_EDITOR) {
            return;
        }
        if (!Array.isArray(plugins)) {
            plugins = new Array(plugins);
        }

        plugins.forEach((plugin) => {
            //插件能不重复
            let findPlugin = this._plugins.some(item => item.name === plugin.name || item === plugin);
            if (findPlugin) {
                return;
            }
            this._plugins.push(plugin);
        });
    }

    private onLoadArrayComplete(path: string[], assets: cc.Asset[], bundle: Bundle) {
        this._plugins.forEach((plugin) => {
            plugin.onLoadArrayComplete(this, path, assets, bundle);
        });
    }
    private onLoadComplete(path: string, asset: cc.Asset, bundle: Bundle) {
        this._plugins.forEach((plugin) => {
            plugin.onLoadComplete(this, path, asset, bundle);
        });
    }

    private onLoadDirComplete(path: string, assets: cc.Asset[], bundle: Bundle) {
        this._plugins.forEach((plugin) => {
            plugin.onLoadDirComplete(this, path, assets, bundle);
        });
    }

    private onRelease(path: string, asset: cc.Asset) {
        this._plugins.forEach((plugin) => {
            plugin.onRelease(this, path, asset);
        });
    }

    private onLoadSceneComplete(sceneName: string, scene: cc.SceneAsset, bundle: Bundle) {
        this._plugins.forEach((plugin) => {
            plugin.onLoadSceneComplete(this, sceneName, scene, bundle);
        });
    }

    // 先对包装层资源进行计数 --
    public releaseAsset<T extends cc.Asset>(path: string, type: CCAssetType<T>, bundle?: Bundle | string) {

        path = this.formatPath(path, type);
        let bundleAsset = this.getBundleAsset(bundle);
        let u_path = this.jointKey(bundleAsset.getPath(), path);

        let asset = this._cache.get(u_path, type);
        if (asset) {
            asset.decRef();
            if (asset.getRefCount() <= 0) {
                let asset = this._cache.delete(u_path, type);
                this.onRelease(asset.getPath(), asset.getAsset());
            }
        }
    }

    // 直接释放引用资源，不对包装层做判断
    public releaseAll() {
        this._cache?.forEach((asset: NormalAsset) => {
            asset.getAsset().decRef();
            this.onRelease(asset.getPath(), asset.getAsset());
        });
        this._cache?.clear();
        this._loadingSet.forEach((id) => {
            this._lastLoadingSet.add(id);
        });
        this._loadingSet.clear();

        // 进行垃圾回收
        cc.sys.garbageCollect();
    }

    // 是否已经加载
    public hasRes(path: string, type: CCAssetType, bundle?: Bundle | string) {
        return !!this.getRes(path, type, bundle);
    }

    // 获取已加载的资源
    public getRes(path: string, type: CCAssetType, bundle?: Bundle | string) {
        let bundleAsset = this.getBundleAsset(bundle);
        let u_path = this.jointKey(bundleAsset.getPath(), path);
        let asset = this._cache.get(u_path, type);
        return asset;
    }

    // 
    private getBundleAsset(bundle?: Bundle | string) {
        if (!bundle) {
            bundle = cc.resources.name;
        } else if (bundle instanceof cc.AssetManager.Bundle) {
            bundle = bundle.name;
        }

        let bundleAsset = AssetLoader.loadedBundles.get(bundle);
        if (!bundleAsset) {
            return null;
        }
        return bundleAsset;
    }

    public preload(paths: string | string[], type: CCAssetType, _bundle?: Bundle | string): void
    public preload(paths: string | string[], type: CCAssetType, _onComplete?: CCCompleteCallbackWithData<RequestItem[]>, _bundle?: Bundle | string): void
    public preload(paths: string | string[], type: CCAssetType, _onProgress: CCProgressCallback, _onComplete: CCCompleteCallbackWithData<RequestItem[]>, _bundle?: Bundle | string): void;
    public preload(paths: string | string[], type: CCAssetType, _onProgress?: CCProgressCallback | CCCompleteCallbackWithData<RequestItem[]> | null | Bundle | string, _onComplete?: CCCompleteCallbackWithData<RequestItem[]> | null | Bundle | string, _bundle?: Bundle | string) {
        let obj = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);

        let { onProgress, onComplete, bundle } = obj;
        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                
                onComplete?.(err,null);
                return;
            }
            
            _onProgress = (finish: number, total: number, item: cc.AssetManager.RequestItem) => {
                onProgress?.(finish, total, item);
            }
            // 保留，否则会在父类里面解析错误
            _onComplete = (error: Error, assets: any) => {
                onComplete?.(error, assets);
            }
            if(!Array.isArray(paths)){
                paths = [paths];
            }
            
            // 对路径进行格式化
            for (let i = 0; i < paths.length; i++) {
                paths[i] = this.formatPath(paths[i],type);
            }
            bundleAsset.getBundle().preload(paths, type, _onProgress, _onComplete);
        });

    }


    public loadArray<T extends cc.Asset>(paths: string[], type: CCAssetType<T>, _bundle?: Bundle | string)
    public loadArray<T extends cc.Asset>(paths: string[], type: CCAssetType<T>, _onComplete?: CCCompleteCallbackWithData<T[]>, _bundle?: Bundle | string)
    public loadArray<T extends cc.Asset>(paths: string[], type: CCAssetType<T>, _onProgress?: CCProgressCallback, _onComplete?: CCCompleteCallbackWithData<T[]>, _bundle?: Bundle | string)
    public loadArray<T extends cc.Asset>(paths: string[], type: CCAssetType<T>, _onProgress?: CCProgressCallback | CCCompleteCallbackWithData<T[]> | Bundle | string, _onComplete?: CCCompleteCallbackWithData<T[]> | Bundle | string, _bundle?: Bundle | string){


        const id = this.computeLoadCount();
    
        let { onProgress, onComplete, bundle } = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);
        
       

        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                onComplete?.(err,null);
                return;
            }
            
            if(!Array.isArray(paths)){
                paths = [paths];
            }
            
            // 对路径进行格式化
            for (let i = 0; i < paths.length; i++) {
                paths[i] = this.formatPath(paths[i],type);
            }
            bundleAsset.getBundle().load(paths, type, (finish: number, total: number, item) => {
                onProgress?.(finish, total, item);
            }, (error: Error, assets: T[]) => {
                if (error) {
                    onComplete?.(error, assets);
                } else {
    
                    assets.forEach((asset) => {


                        if (this._lastLoadingSet.has(id)) {
                            asset.addRef();
                            asset.decRef();
                            return;
                        }
                        // @ts-ignore
                        let info = bundleAsset.getBundle().getAssetInfo(asset._uuid);
                        // @ts-ignore
                        let path = info.path;
                        let u_path = this.jointKey(bundleAsset.getPath(), path);
                        let noramlAsset = this._cache.get(u_path, type);
                        if (!noramlAsset) {
                            noramlAsset = new NormalAsset(path, asset, bundleAsset);
                            this._cache.set(u_path, noramlAsset);
                        }
                        noramlAsset.addRef();
                    });

                    // 检查是否有当前id
                    if (this.checkLastLoadCount(id)) {
                        return;
                    }
    
                    this.onLoadArrayComplete(paths, assets, bundleAsset.getBundle());
                    onComplete?.(error, assets);
                }
            });
        });

    }

    public load<T extends cc.Asset>(path: string, type: CCAssetType<T>, _bundle?: Bundle | string)
    public load<T extends cc.Asset>(path: string, type: CCAssetType<T>, _onComplete?: CCCompleteCallbackWithData<T>, _bundle?: Bundle | string)
    public load<T extends cc.Asset>(path: string, type: CCAssetType<T>, _onProgress?: CCProgressCallback, _onComplete?: CCCompleteCallbackWithData<T>, _bundle?: Bundle | string)
    public load<T extends cc.Asset>(path: string, type: CCAssetType<T>, _onProgress?: CCProgressCallback | CCCompleteCallbackWithData<T> | Bundle | string, _onComplete?: CCCompleteCallbackWithData<T> | Bundle | string, _bundle?: Bundle | string) {

            const id = this.computeLoadCount();


            // 对路径进行格式化
            path = this.formatPath(path, type);

            let obj = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);
            let { onProgress, onComplete, bundle } = obj;
            this.loadBundleAssets(bundle,(err,bundleAsset)=>{
                if(err){
                    onComplete?.(err,null);
                    return;
                }

                let u_path = this.jointKey(bundleAsset.getPath(), path);
                let asset = this._cache.get(u_path, type);
                if (asset) {
                    asset.addRef();
                    let _asset: cc.Asset = asset.getAsset();
                    onComplete?.(null, _asset);
                    return;
                }
    
                bundleAsset.getBundle().load(path, type as any, (finish: number, total: number, item) => {
                    onProgress?.(finish, total, item);
                }, (error: Error, assets: T) => {
                    if (error) {
                        onComplete?.(error, assets);
                    } else {
                        
                        if(this.checkLastLoadCount(id)){
                            assets.addRef();
                            assets.decRef();
                            return;
                        }

                        let asset = new NormalAsset(path, assets, bundleAsset);
                        asset.addRef();
                        this._cache.set(u_path, asset);
                        this.onLoadComplete(path, assets, bundleAsset.getBundle());
                        onComplete?.(error, assets);
                    }
                });
            });

    }

    public preloadDir(dir: string, type: CCAssetType | null, _bundle?: Bundle | string)
    public preloadDir(dir: string, type: CCAssetType | null, _onProgress: CCProgressCallback | null, _onComplete: CCCompleteCallbackWithData<RequestItem[]> | null, _bundle?: Bundle | string)
    public preloadDir(dir: string, type: CCAssetType | null, _onComplete?: CCCompleteCallbackWithData<RequestItem[]> | null, _bundle?: Bundle | string)
    public preloadDir(dir: string, type?: CCAssetType, _onProgress?: CCProgressCallback | CCCompleteCallbackWithData<RequestItem[]> | null | Bundle | string, _onComplete?: CCCompleteCallbackWithData<RequestItem[]> | null | Bundle | string, _bundle?: Bundle | string
    ) {
        let { onProgress, onComplete, bundle } = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);
        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                onComplete?.(err,null);
                return;
            }
            _onProgress = (finish: number, total: number, item: cc.AssetManager.RequestItem) => {
                onProgress?.(finish, total, item);
            }
            // 保留，否则会在父类里面解析错误
            _onComplete = (error: Error, assets: any) => {
                onComplete?.(error, assets);
            }
    
            bundleAsset.getBundle().preloadDir(dir, type, _onProgress, _onComplete);
        });

    }
    public loadDir<T extends cc.Asset>(dir: string, type: CCAssetType<T>, _bundle?: Bundle | string)
    public loadDir<T extends cc.Asset>(dir: string, type: CCAssetType<T>, _onComplete?: CCCompleteCallbackWithData, _bundle?: Bundle | string)
    public loadDir<T extends cc.Asset>(dir: string, type: CCAssetType<T>, _onProgress?: CCProgressCallback, _onComplete?: CCCompleteCallbackWithData, _bundle?: Bundle | string)
    public loadDir<T extends cc.Asset>(dir: string, type: CCAssetType<T>, _onProgress?: CCProgressCallback | CCCompleteCallbackWithData | Bundle | string, _onComplete?: CCCompleteCallbackWithData | Bundle | string, _bundle?: Bundle | string) {

        const id = this.computeLoadCount();
       

        let { onProgress, onComplete, bundle } = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);

        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                onComplete?.(err,null);
                return;
            }
            bundleAsset.getBundle().loadDir(dir, type as any, (finish: number, total: number, item: RequestItem) => {
                // this.onProgress(finish,total,item);
                onProgress?.(finish, total, item);
            }, (error: Error, assets: Array<T>) => {
                if(error){
                    onComplete?.(error, assets);
                    return;
                }
                assets.forEach((asset) => {
                    // 
                    if (this._lastLoadingSet.has(id)) {
                        asset.addRef();
                        asset.decRef();
                        return;
                    }

                    // @ts-ignore
                    let info = bundleAsset.getBundle().getAssetInfo(asset._uuid);
                    // @ts-ignore
                    let path = info.path;
                    let u_path = this.jointKey(bundleAsset.getPath(), path);
                    let noramlAsset = this._cache.get(u_path, type);
                    if (!noramlAsset) {
                        noramlAsset = new NormalAsset(path, asset, bundleAsset);
                        this._cache.set(u_path, noramlAsset);
                    }
                    noramlAsset.addRef();
                });
    
                if (this.checkLastLoadCount(id)) {
                    return;
                }
                this.onLoadDirComplete(dir, assets, bundleAsset.getBundle());
                onComplete?.(error, assets);
            });
        });
    }



    public loadScene(sceneName: string, _bundle?: cc.AssetManager.Bundle | string)
    public loadScene(sceneName: string, _onComplete?: CCCompleteCallbackWithData, _bundle?: cc.AssetManager.Bundle | string)
    public loadScene(sceneName: string, _onProgress: CCProgressCallback, _onComplete?: CCCompleteCallbackWithData, _bundle?: cc.AssetManager.Bundle | string)
    public loadScene(sceneName: string, _onProgress: CCProgressCallback | CCCompleteCallbackWithData | cc.AssetManager.Bundle | string, _onComplete?: CCCompleteCallbackWithData | cc.AssetManager.Bundle | string, _bundle?: cc.AssetManager.Bundle | string) {
     
        let { onProgress, onComplete, bundle } = this.parsingLoadArgs(_onProgress, _onComplete, _bundle);
        if (!bundle) {
            bundle = cc.assetManager.bundles.find((bundle) => {
                return !!bundle.getSceneInfo(sceneName);
            });
        }
        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                onComplete?.(err, null);
                return;
            }
            // 加载 场景资源
            bundleAsset.getBundle().loadScene(sceneName, (finish: number, total: number, item: cc.AssetManager.RequestItem) => {
                onProgress?.(finish, total, item);
            }, (error: Error, assets: cc.SceneAsset) => {
                // await this.onComplete(error,assets);

                this.onLoadSceneComplete(sceneName, assets, bundleAsset.getBundle());
                onComplete?.(error, assets);
            });
        });
    }


    public preloadScene(sceneName: string, bundle?: cc.AssetManager.Bundle | string)
    public preloadScene(sceneName: string, onComplete?: CCCompleteCallbackNoData, bundle?: cc.AssetManager.Bundle | string)
    public preloadScene(sceneName: string, options?: CCIAssetOptions | null, bundle?: cc.AssetManager.Bundle | string)
    public preloadScene(sceneName: string, options?: CCIAssetOptions | null, onComplete?: CCCompleteCallbackNoData, bundle?: cc.AssetManager.Bundle | string)
    public preloadScene(sceneName: string, onProgress?: CCProgressCallback, onComplete?: CCCompleteCallbackNoData, bundle?: cc.AssetManager.Bundle | string)
    public preloadScene(sceneName: string, _options?: CCIAssetOptions | null | CCProgressCallback | cc.AssetManager.Bundle | string, _onProgress?: CCProgressCallback | CCCompleteCallbackNoData | cc.AssetManager.Bundle | string, _onComplete?: CCCompleteCallbackNoData | cc.AssetManager.Bundle | string, _bundle?: cc.AssetManager.Bundle | string) {


        let { options, onProgress, onComplete, bundle } = this.parseParameters(_options, _onProgress, _onComplete, _bundle);
        if (!bundle) {
            bundle = cc.assetManager.bundles.find((bundle) => {
                return !!bundle.getSceneInfo(sceneName);
            });
        }
        this.loadBundleAssets(bundle,(err,bundleAsset)=>{
            if(err){
                onComplete?.(err, null);
                return;
            }
            // 加载 场景资源
            bundleAsset.getBundle().preloadScene(sceneName, options, (finish: number, total: number, item: cc.AssetManager.RequestItem) => {
                onProgress?.(finish, total, item);
            }, (error: Error) => {
                // await this.onComplete(error,assets);
                onComplete?.();
            });
        });
    }

    // 计算id
    protected computeLoadCount() {
        const id = this._loadCount++;
        this._loadingSet.add(id);
        return id;
    }
    // 检查在 _lastLoadingSet  是否包含 id
    protected checkLastLoadCount(id) {
        let isHas = false;
        if (this._lastLoadingSet.has(id)) {
            this._lastLoadingSet.delete(id);
            isHas = true;
        }
        if (!isHas) {
            if (this._loadingSet.has(id)) {
                this._loadingSet.delete(id);
            }
        }
        return isHas;
    }

    protected jointKey(bundle: string, path) {
        let u_path = `${bundle}#${path}`;
        return u_path;
    }

    public parsingLoadArgs(...args) {
        let onProgress: CCProgressCallback, onComplete: CCCompleteCallbackWithData, bundle: string | Bundle;
        let _onProgress = args[0];
        let _onComplete = args[1];
        let _bundle = args[2];

        if (_onProgress && _onComplete && _bundle) {
            onProgress = _onProgress;
            onComplete = _onComplete;
            bundle = _bundle;
        } else {
            if (typeof _onProgress === 'function') {
                if (typeof _onComplete === 'function') {
                    onProgress = _onProgress;
                    onComplete = _onComplete;
                } else if (typeof _onComplete === 'undefined') {
                    onComplete = _onProgress;
                } else {
                    onComplete = _onProgress;
                    bundle = _onComplete;
                }
            } else {
                bundle = _onProgress;
            }
        }
        let obj = { onProgress, onComplete, bundle };
        return obj;
    }

    public parseParameters(options, onProgress, onComplete, bundle) {

        if (typeof onComplete === 'function') {
        } else if (typeof onComplete === 'string' || onComplete instanceof cc.AssetManager.Bundle) {
            bundle = onComplete;
            onComplete = onProgress;
            onProgress = null
        } else if (!onComplete) {
            if (typeof onProgress === 'function') {
                onComplete = onProgress;
                onProgress = null
            } else if (typeof onProgress === 'string' || onProgress instanceof cc.AssetManager.Bundle) {
                bundle = onProgress;
                onProgress = null;
            } else if (!onProgress) {
                if (typeof options === 'function') {
                    onComplete = options;
                    options = null;
                } else if (typeof options === 'string' || options instanceof cc.AssetManager.Bundle) {
                    bundle = options;
                    options = null;
                }
            }
        }

        options = options || Object.create(null);
        return { options, onProgress, onComplete, bundle };
    }

    isNull(object) {
        return object === null || object === undefined;
    }

    isObject(object) {
        return Object.prototype.toString.call(object) === '[object Object]'
    }

    formatPath<T extends cc.Asset>(path: string, type: CCAssetType<T>) {

        if (cc.js.getClassName(type) === cc.js.getClassName(cc.SpriteFrame)) {
            path += "/spriteFrame";
        } else if (cc.js.getClassName(type) === cc.js.getClassName(cc.Texture2D)) {
            path += "/texture";
        }

        return path;
    }
    /**
     * 在编辑器中加载资源
     */
    loadResInEditor(path, type, callback) {
        let self = this;
        if (CC_EDITOR) {
            self.getUUIDFromMeta(path, type, function (uuid) {
                cc.assetManager.loadAny({
                    uuid: uuid
                }, callback);
            });
        }
    }

    /*
   编辑器获取读取meta文件获取 uuid
   */
    getUUIDFromMeta(filepath, type, callback) {
        if (CC_EDITOR) {
            // @ts-ignore
            var path = require('path');
            // @ts-ignore
            var fs = require('fs');
            // @ts-ignore
            let projectPath = Editor.Project.path || Editor.projectPath;

            let exts = {};
            // @ts-ignore
            exts[cc.Prefab] = "prefab";
            // @ts-ignore
            exts[cc.JsonAsset] = "json";
            // @ts-ignore
            exts[cc.SpriteFrame] = "png";


            let absolutePath = path.join(projectPath, "assets", "resources", `${filepath}.${exts[type]}.meta`);
            // cc.log('绝对路径', absolutePath);
            if (!fs.existsSync(absolutePath)) {
                cc.warn(`[${absolutePath}]file is not exist `);
                return;
            }

            fs.readFile(absolutePath, function (err, data) {
                if (err) {
                    cc.warn("parse uuid error = ", err || err.message);
                    return;
                }
                let dataStr = data.toString();
                let json = JSON.parse(dataStr);
                // cc.log("读取文件: ", dataStr);

                if (type === cc.SpriteFrame) {
                    let filearr = filepath.split("/");
                    let filename = filearr[filearr.length - 1];
                    let uuid = json.subMetas[filename].uuid;
                    // cc.log("filename: ", filename);
                    // cc.log("lgUtils-> uuid: ", uuid);
                    callback(uuid);
                } else {
                    let uuid = json.uuid;
                    // cc.log('lgUtils-> uuid: ', uuid);
                    callback(uuid);
                }
            });
        }
    }




    private static _instance: AssetLoader = null
    public static getInstance(): AssetLoader {
        if (!this._instance) {
            this._instance = new AssetLoader();
        }
        return this._instance;
    }
}

export const loader = AssetLoader.getInstance();